﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ITAP.Core.Modelling.Primitives
{
    public enum EndedPrimitivePoints
    {
        Start, 
        End
    }
    /// <summary>
    /// Интерфейс примитива с двумя концами.
    /// </summary>
    public interface IEndedPrimitive : IPrimitive
    {
        /// <summary>
        /// Начальная точка примитива с двумя концами.
        /// </summary>
        /// <remarks>
        /// В основном используется для задания точек среза. В Line эквивалентно MoveStartPoint(), а в Circle 
        /// изменение StartPoint изменит точку, не меняя положения центра, т.е. просто изменит положение 
        /// точки НА окружности. Для обыкновенного перемещения начала используйте метод MoveStartPoint(ModelPoint).
        /// </remarks>
        ModelPoint StartPoint { get; set; }
        /// <summary>
        /// Конечная точка примитива с двумя концами.
        /// </summary>
        /// <remarks>
        /// В основном используется для задания точек среза. В Line эквивалентно MoveEndPoint(), а в Circle 
        /// изменение EndPoint изменит точку, не меняя положения центра, т.е. просто изменит положение 
        /// точки НА окружности. Для обыкновенного перемещения начала используйте метод MoveEndPoint(ModelPoint).
        /// </remarks>
        ModelPoint EndPoint { get; set; }
        /// <summary>
        /// Перемещение начальной точки примитива в указанную позицию.
        /// </summary>
        /// <param name="destination">Позиция, в которую надо переместить начальную точку примитива.</param>
        void MoveStartPoint(ModelPoint destination);
        /// <summary>
        /// Перемещение конечной точки примитива в указанную позицию.
        /// </summary>
        /// <param name="destination">Позиция, в которую надо переместить конечную точку примитива.</param>
        void MoveEndPoint(ModelPoint destination);
        /// <summary>
        /// Задание длины примитива с двумя концами.
        /// </summary>
        /// <param name="basePoint">Основная точка, от которой будет увеличиваться или уменьшаться длина.</param>
        /// <param name="length">Задаваемая длина примитива.</param>
        void SetLength(EndedPrimitivePoints basePoint, double length);
        /// <summary>
        /// Получение минимального расстояния между точкой и сегментом примитива (не прямой или окружностью примитива!). В случае, если 
        /// невозможно спроектировать точку на сегмент (проекция не попадает в сегмент примитива), возвращается ближайшая из конечных точек.
        /// </summary>
        /// <param name="point">Точка, для которой производится расчёт.</param>
        /// <returns>Минимальное расстояние от точки до сегмента.</returns>
        double GetDistanceFromSegmentToPoint(ModelPoint point);
        /// <summary>
        /// Получение точек проекции на сегмент. Их шесть, поскольку в случае арки может получиться две точки проекции, а поскольку арка 
        /// с толщиной имеет в своём основании три примитива, то необходимо вернуть проекцию на каждый примитив, итого получается 6 точек.
        /// </summary>
        /// <param name="point">Точка, для которой ищутся точки проекции.</param>
        /// <returns></returns>
        ModelPoint[] GetProjectionPoints(ModelPoint point);
        /// <summary>
        /// Быстрый поиск проекции на примитив. Возвращает ближайшую к точке точку проекции.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        ModelPoint GetProjectionPoint(ModelPoint point);
        /// <summary>
        /// Пересечение основных линий примитивов. Работает по аналогии с Intersect(), только в качестве результата будут точки пересечения 
        /// центральных линий примитивов.
        /// </summary>
        /// <param name="primitive">Примитив, с которым надо пересечь данный примитив.</param>
        /// <param name="endForSortPoints">Сортировка точек. Точки сортируются по близости к началу, либо к концу данного примитива.</param>
        /// <returns>Точки пересечения основных линий.</returns>
        ModelPoint[] IntersectMainLines(IEndedPrimitive primitive, EndType endForSortPoints);
        ModelPoint[] IntersectMainLines(Line line, EndType endForSortPoints);
        ModelPoint[] IntersectMainLines(Circle circle, EndType endForSortPoints);
        ModelPoint[] IntersectMainLinesPassive(Line line, EndType endForSortPoints);
        ModelPoint[] IntersectMainLinesPassive(Circle circle, EndType endForSortPoints);
        /// <summary>
        /// Определяет, находится ли точка внутри сегмента, учитывая совпадение с конечными точками сегмента.
        /// </summary>
        /// <param name="point">Точка, для которой производится проверка.</param>
        /// <param name="considerEnds">Если true, то при совпадении точки-аргумента с одной из конечных точек будет считаться, что 
        /// точка принадлежит сегменту, если false, то в этом случае будет считаться, что точка не принадлежит сегменту.</param>
        /// <returns>True, если точка принадлежит сегменту, иначе false.</returns>
        bool IsPointInSegment(ModelPoint point, bool considerEnds);
        /// <summary>
        /// Высота примитива (высота основной линии арки).
        /// </summary>
        double Height { get; set; }
        /// <summary>
        /// Меняет местами начальную и конечную точки примитива, при этом учитывая высоту примитива, таким образом внешне положение 
        /// примитива не меняется. Часто необходимо в случае арки, поскольку внешне одинаковые арки можно задать двумя
        /// способами - с положительной высотой и одним положением начала и конца, и с отрицательной высотой и другим положением начала и конца.
        /// </summary>
        /// <returns>Новый примитив.</returns>
        IEndedPrimitive ExchangeEnds();
        /// <summary>
        /// Разбивание примитива на несколько равных по длине примитивов.
        /// </summary>
        /// <param name="numberOfPieces">Количество "кусочков", на которые будет разбит примитив.</param>
        /// <param name="gapBetweenPieces">Расстояние между концами кусочков.</param>
        /// <returns></returns>
        IEndedPrimitive[] BreakToPieces(int numberOfPieces, double gapBetweenPieces);
        /// <summary>
        /// Разбиение примитива в данной точке на два примитива.
        /// </summary>
        /// <param name="point">Точка, в которой надо разбивать.</param>
        /// <param name="gapBetweenPieces">Расстояние между концами новых примитивов.</param>
        /// <returns>Массив новых примитивов.</returns>
        IEndedPrimitive[] BreakInPoint(ModelPoint point, double gapBetweenPieces);
        /// <summary>
        /// Получение длины примитива от начала примитива до проекции данной точки на примитив.
        /// </summary>
        /// <param name="point">Точка, проекция которой будет использована.</param>
        /// <returns>Длина примитива от начала примитива до точки проекции.</returns>
        double GetPathLengthFromProjectionToStart(ModelPoint point);
        /// <summary>
        /// Используется в движке размеров, для внутреннего использования.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        bool IsPointOnPrimitive(ModelPoint point);
    }
}
